<!-- 图片裁剪组件 license by http://eleadmin.com -->
<template>
  <div class="ele-cropper">
    <div class="ele-cropper-group">
      <!-- 裁剪区域 -->
      <div class="ele-cropper-img-group">
        <img ref="imageEl" :src="src" style="max-width: 100%" alt="" />
      </div>
      <!-- 预览区域 -->
      <div
        v-if="showPreview"
        ref="previewEl"
        :style="{ width: previewWidth + 20 + 'px' }"
        class="ele-cropper-preview-group"
      >
        <div
          class="ele-cropper-preview"
          :style="{
            width: previewWidth + 'px',
            height: previewWidth / (aspectRatio || 1) + 'px'
          }"
        >
        </div>
        <div
          v-if="aspectRatio === 1"
          class="ele-cropper-preview ele-cropper-preview-circle"
          :style="{
            width: previewWidth + 'px',
            height: previewWidth / (aspectRatio || 1) + 'px'
          }"
        >
        </div>
        <template v-else>
          <div
            class="ele-cropper-preview"
            :style="{
              width: previewWidth + 'px',
              height: ((previewWidth / 3) * 2 - 10) / (aspectRatio || 1) + 'px'
            }"
          >
          </div>
          <div
            class="ele-cropper-preview"
            :style="{
              width: previewWidth + 'px',
              height: previewWidth / 3 / (aspectRatio || 1) + 'px',
              marginLeft: '10px'
            }"
          >
          </div>
        </template>
      </div>
    </div>
    <!-- 操作按钮 -->
    <ele-cropper-tools
      :tools="tools"
      :ok-text="okText || t('ele.cropper.ok')"
      @crop="crop"
      @move-b="moveB"
      @move-l="moveL"
      @move-r="moveR"
      @move-t="moveT"
      @reset="reset"
      @rotate-l="rotateL"
      @rotate-r="rotateR"
      @scale-x="scaleX"
      @scale-y="scaleY"
      @replace="replace"
      @zoom-in="zoomIn"
      @zoom-out="zoomOut"
    />
  </div>
</template>

<script>
  import Cropper from 'cropperjs';
  import Locale from 'element-ui/lib/mixins/locale';
  import EleCropperTools from './components/ele-cropper-tools.vue';

  export default {
    name: 'EleCropper',
    components: { EleCropperTools },
    mixins: [Locale],
    emits: ['crop'],
    props: {
      // 图片地址
      src: String,
      // 裁剪比例
      aspectRatio: {
        type: Number,
        default: 1
      },
      // 裁剪组件模式
      viewMode: {
        type: Number,
        default: 0
      },
      // 拖动模式
      dragMode: {
        type: String,
        default: 'crop'
      },
      // 裁剪框的初始宽高比
      initialAspectRatio: Number,
      // 容器最小宽度
      minContainerWidth: {
        type: Number,
        default: 200
      },
      // 容器最小高度
      minContainerHeight: {
        type: Number,
        default: 100
      },
      // 画布最小宽度
      minCanvasWidth: {
        type: Number,
        default: 0
      },
      // 画布最小高度
      minCanvasHeight: {
        type: Number,
        default: 0
      },
      // 裁剪盒子最小宽度
      minCropBoxWidth: {
        type: Number,
        default: 0
      },
      // 裁剪盒子最小高度
      minCropBoxHeight: {
        type: Number,
        default: 0
      },
      // 裁剪输出宽度
      croppedWidth: Number,
      // 裁剪输出高度
      croppedHeight: Number,
      // 裁剪输出最小宽度
      croppedMinWidth: {
        type: Number,
        default: 0
      },
      // 裁剪输出最小高度
      croppedMinHeight: {
        type: Number,
        default: 0
      },
      // 裁剪输出最大宽度
      croppedMaxWidth: Number,
      // 裁剪输出最大高度
      croppedMaxHeight: Number,
      // 裁剪输出的填充色
      croppedFillColor: {
        type: String,
        default: 'transparent'
      },
      //
      imageSmoothingEnabled: Boolean,
      // 裁剪输出的质量
      imageSmoothingQuality: String,
      // 是否返回blob数据
      toBlob: Boolean,
      // 允许上传的图片类型
      accept: String,
      // 操作按钮布局
      tools: String,
      // 是否显示预览组件
      showPreview: {
        type: Boolean,
        default: true
      },
      // 完成按钮文字
      okText: String,
      // 预览组件宽度
      previewWidth: {
        type: Number,
        default: 120
      }
    },
    data() {
      return {
        // cropper实例
        ins: null,
        // 当前图片类型
        imgType: 'image/jpeg',
        // 水平翻转值
        scaleXValue: -1,
        // 垂直翻转值
        scaleYValue: -1
      };
    },
    mounted() {
      this.src && this.render();
    },
    methods: {
      /* 渲染 */
      render() {
        this.destroy();
        const options = {
          aspectRatio: this.aspectRatio,
          viewMode: this.viewMode,
          dragMode: this.dragMode,
          initialAspectRatio: this.initialAspectRatio,
          minContainerWidth: this.minContainerWidth,
          minContainerHeight: this.minContainerHeight,
          minCanvasWidth: this.minCanvasWidth,
          minCanvasHeight: this.minCanvasHeight,
          minCropBoxWidth: this.minCropBoxWidth,
          minCropBoxHeight: this.minCropBoxHeight
        };
        if (this.showPreview) {
          options.preview = this.$refs.previewEl?.children;
        }
        this.ins = new Cropper(this.$refs.imageEl, options);
      },
      /* 放大 */
      zoomIn() {
        this.ins && this.ins.zoom(0.1);
      },
      /* 缩小 */
      zoomOut() {
        this.ins && this.ins.zoom(-0.1);
      },
      /* 左移 */
      moveL() {
        this.ins && this.ins.move(-10, 0);
      },
      /* 右移 */
      moveR() {
        this.ins && this.ins.move(10, 0);
      },
      /* 上移 */
      moveT() {
        this.ins && this.ins.move(0, -10);
      },
      /* 下移 */
      moveB() {
        this.ins && this.ins.move(0, 10);
      },
      /* 左旋 */
      rotateL() {
        this.ins && this.ins.rotate(-45);
      },
      /* 右旋 */
      rotateR() {
        this.ins && this.ins.rotate(45);
      },
      /* 左右镜像 */
      scaleX() {
        this.ins && this.ins.scaleX(this.scaleXValue);
        this.scaleXValue = -this.scaleXValue;
      },
      /* 上下镜像 */
      scaleY() {
        this.ins && this.ins.scaleY(this.scaleYValue);
        this.scaleYValue = -this.scaleYValue;
      },
      /* 重置 */
      reset() {
        this.ins && this.ins.reset();
      },
      /* 裁剪 */
      crop() {
        const result = this.ins?.getCroppedCanvas({
          width: this.croppedWidth,
          height: this.croppedHeight,
          minWidth: this.croppedMinWidth,
          minHeight: this.croppedMinHeight,
          maxWidth: this.croppedMaxWidth,
          maxHeight: this.croppedMaxHeight,
          fillColor: this.croppedFillColor,
          imageSmoothingEnabled: this.imageSmoothingEnabled,
          imageSmoothingQuality: this.imageSmoothingQuality
        });
        if (result) {
          if (this.toBlob) {
            result.toBlob((blob) => {
              this.$emit('crop', blob);
            }, this.imgType);
          } else {
            this.$emit('crop', result.toDataURL(this.imgType));
          }
        } else {
          this.$emit('crop');
        }
      },
      /* 替换图片 */
      replace({ data, type }) {
        this.imgType = type;
        this.ins && this.ins.replace(data);
      },
      /* 销毁 */
      destroy() {
        this.ins && this.ins.destroy();
        this.ins = null;
      }
    },
    watch: {
      src(value) {
        if (value) {
          if (this.ins) {
            this.ins.replace(value);
          } else {
            this.$nextTick(() => {
              this.render();
            });
          }
        } else {
          this.destroy();
        }
      }
    },
    unmounted() {
      this.destroy();
    }
  };
</script>

<style lang="scss">
  @import '~cropperjs/dist/cropper.css';

  .ele-cropper {
    .ele-cropper-group {
      display: flex;
    }

    .ele-cropper-img-group {
      flex: 1;
    }

    .ele-cropper-preview-group {
      font-size: 0;
      text-align: right;
    }

    .ele-cropper-preview {
      display: inline-block;
      border: 1px solid hsla(0, 0%, 80%, 0.6);
      vertical-align: top;
      overflow: hidden;
    }

    .ele-cropper-preview-circle {
      margin-top: 20px;
      border-radius: 50%;
    }
  }

  /* 小屏幕适应 */
  @media screen and (max-width: 768px) {
    .ele-cropper .ele-cropper-preview-group {
      display: none;
    }
  }
</style>
